package com.novoda.runbuddy.activity.stopwatch;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;

import com.novoda.runbuddy.model.Run;
import com.novoda.runbuddy.provider.ActivityID;
import com.novoda.runbuddy.service.IRemoteServiceCallback;
import com.novoda.runbuddy.service.IRemoteStopClockService;
import com.novoda.runbuddy.service.RemoteStopClockService;
import com.novoda.runbuddy.service.IRemoteStopClockService.Stub;
import com.novoda.runbuddy.util.DBHelperImpl;

/*******************************************************************************
 * 
 * @author Justin Morgenthau
 * 
 *			RemoteStopClockServiceInterface is a singleton class which provides a
 *			common interface between the client application and the StopClock
 *			service.
 * 
 *			Each activity in the application should call the activityStarted()
 *			method within its onStart() method, and call activityStopped() within
 *			its onStop method. This allows RemoteStopClockServiceInterface to
 *			shut down the service as needed when all activities have stopped.
 *         
 *			NOTE: startTime and clockRunning are maintained both in the client and in
 *			the service. This is done for the benefit
 *			of runtime performance, but requires care that the values do not get out
 *			of sync.
 * 
 */

public class RemoteStopClockProxy {

	private static final String						TAG				= "[RBuddy]:" + "RemoteStopClockProxy";
	private static RemoteStopClockProxy	instance		= null;
	private int										listeningActivityCount;
	private Handler									updateHandler	= null;
	private Activity								currentActivity;
	private Activity								boundActivity;

	private long									startTime		= 0;
	private long									stopTime		= 0;
	private boolean									clockRunning	= false;

	private DBHelperImpl							dbHelper;
	private Run										currentRun		= null;

	// private constructor so nobody else creates an instance
	private RemoteStopClockProxy() {
		listeningActivityCount = 0;

	}

	// messageService, messageConnection, and mCallback are used to communicate
	// with the service
	private IRemoteStopClockService	remoteStopClockService;
	private IRemoteServiceCallback	remoteStopClockServiceCallback	= new IRemoteServiceCallback.Stub() {
		public void updatedStopwatch(String time) {
			if (updateHandler != null) {
				// Message
				// msg =
				// Message.
				// obtain(
				// updateHandler
				// ,
				//ActivityID.
				// UPDATESTOPCLOCK
				// ,
				// time);
				// msg.
				// sendToTarget
				// ();
			}
		}

		public void updatedRoute(Location newLocation) {
			Log.i(TAG, "Received location update longitude=["	+ newLocation.getLongitude() +"] latitude=["+ newLocation.getLatitude() + "]" );
			if (updateHandler != null) {
				Message msg = Message.obtain(updateHandler,	ActivityID.LOCATION_UPDATE, newLocation);
				msg.sendToTarget();
			}
		}
	};

	private ServiceConnection		messageConnection  = new ServiceConnection() {
			
		public void onServiceConnected(ComponentName className, IBinder service) {
				remoteStopClockService = IRemoteStopClockService.Stub.asInterface(service);
	
				try {
						remoteStopClockService.registerStopClockUpdateListener(remoteStopClockServiceCallback);
						startTime = remoteStopClockService.getStartTime();
						stopTime = remoteStopClockService.getStopTime();
						clockRunning = remoteStopClockService.isStopClockRunning();
						if (clockRunning) {
							populateCurrentJog();
						} else {
							currentRun = null;
						}

					} catch (RemoteException e) {
						Log.v(TAG, "Tried to register with dead Object");
					}

				}

				public void onServiceDisconnected(ComponentName className) {
					remoteStopClockService = null;
				}

	};

	private void populateCurrentJog() {
		long id = -666; //Unreachable number to intantiate the jog ID. //Get the next Jog ID
		
		try {
			id = remoteStopClockService.getCurrentJogId();
			currentRun = dbHelper.fetchFullJogEntry(Long.toString(id));
		} catch (RemoteException e) {
			Log.v(TAG, "Tried to fetch Jog Id with dead Object");
		}
		
		if (currentRun == null) {
			Log.w(TAG, "Could Not Get current jog with id:" + id);
		}
	}

	// getInstance() returns the singleton instance
	public static RemoteStopClockProxy getInstance() {
		if (instance == null) {
			instance = new RemoteStopClockProxy();
		}
		return instance;
	}

	public void registerListener(Activity activity) {
		if (listeningActivityCount == 0) {
			dbHelper = new DBHelperImpl(activity);
			activity.startService(new Intent(activity, RemoteStopClockService.class));
			activity.bindService(new Intent(IRemoteStopClockService.class.getName()), messageConnection, Context.BIND_AUTO_CREATE);
			boundActivity = activity;
		}

		listeningActivityCount++;
		currentActivity = activity;
		Log.i(TAG, "Listening Activity count incremented to " + listeningActivityCount);
	}

	public void unregisterListener(Activity activity) {
		listeningActivityCount--;
		Log.i(TAG, "Listening Activities count decremented to " + listeningActivityCount);

		if (listeningActivityCount == 0) {

			try {
				remoteStopClockService.unregisterStopClockUpdateListener(remoteStopClockServiceCallback);

				// send service message to self-terminate if idle
				remoteStopClockService.terminateServiceIfIdle();
				boundActivity.unbindService(messageConnection);
			} catch (RemoteException e) {
				Log.d(TAG, "Tried to unregister with dead Object");
			}
		}

		if (currentActivity == activity) {
			currentActivity = null;
		}
	}

	public void startStopClock() {
		try {
			startTime = System.currentTimeMillis();
			stopTime = 0;
			Log.i(TAG, "Set startTime " + startTime);
			remoteStopClockService.startStopClock(startTime);
			clockRunning = true;
			populateCurrentJog();

		} catch (RemoteException e) {
			Log.d(TAG, "Tried to start clock with dead Object");
		}
		if (currentActivity != null) {
			currentActivity.sendBroadcast(new Intent(ActivityID.STOPWATCH_STARTED));
		}

	}

	public void stopStopClock() {
		try {
			stopTime = System.currentTimeMillis();
			remoteStopClockService.stopStopClock(stopTime);
			Log.i(TAG, "Set stopTime " + stopTime);
			clockRunning = false;
		} catch (RemoteException e) {
			Log.d(TAG, "Tried to stop clock with dead Object");
		}
		if (currentActivity != null) {
			currentActivity.sendBroadcast(new Intent(ActivityID.STOPWATCH_STOPPED));
		}
		currentRun = null;
	}

	public void setUpdateHandler(Handler handler) {
		updateHandler = handler;
	}

	public void clearUpdateHandler(Handler handler) {
		if (updateHandler == handler)
			updateHandler = null;
	}

	public synchronized String getElapsedTime() {

		Long elapsedTime;

		if (clockRunning) {
			elapsedTime = (System.currentTimeMillis() - this.startTime);
		} else {
			if (stopTime > 0L) {
				elapsedTime = (stopTime - this.startTime);
			} else {
				elapsedTime = 0L;
			}
		}

		return StopwatchTools.getStringInExpectedFormat(elapsedTime);
	}

	public boolean isClockRunning() {
		return clockRunning;
	}

	public long getStartTime() {
		return startTime;
	}

	public Run getCurrentJog() {
		populateCurrentJog();
		return currentRun;
	}

	public void setCannedRouteEnabled(boolean enabled) {
		try {
			remoteStopClockService.setCannedRouteEnabled(enabled);
		} catch (RemoteException e) {
		}
	}

	public boolean getCannedRouteEnabled() {
		try {
			return remoteStopClockService.getCannedRouteEnabled();
		} catch (RemoteException e) {
		}

		return false;
	}

	public void setCannedRouteId(int id) {
		try {
			remoteStopClockService.setCannedRouteId(id);
		} catch (RemoteException e) {
		}
	}

	public int getCannedRouteId() {
		try {
			return remoteStopClockService.getCannedRouteId();
		} catch (RemoteException e) {
		}

		return 1;
	}

	public void setCannedRouteSpeed(double speed) {
		try {
			remoteStopClockService.setCannedRouteSpeed(speed);
		} catch (RemoteException e) {
		}
	}

	public double getCannedRouteSpeed() {
		try {
			return remoteStopClockService.getCannedRouteSpeed();
		} catch (RemoteException e) {
		}

		return 0.0;
	}

}
